module lab8 (
    input clk,
    input rst,
    input [3:0] key_row,
    input [1:0] m,
    input [1:0] ra,
    input key_sel,
    input [2:0] btn,
    output [3:0] key_col,
    output [7:0] seg,
    output [2:0] sel
);

  wire [3:0] val;
  wire is_pressed;

  key u_key (
      clk,
      rst,
      key_row,
      key_col,
      val,
      is_pressed
  );

  reg  [ 7:0] pc;
  wire [15:0] rom_data;

  rom u_rom (
      .clock(clk),
      .address(pc),
      .q(rom_data)
  );

  reg [ 7:0] en;
  reg [31:0] data;
  display u_display (
      clk,
      rst,
      data,
      en,
      seg,
      sel
  );

  reg page;

  always @(negedge clk or negedge rst) begin
    if (!rst) begin
      page <= 1'b0;
    end else if (!btn[0]) begin
      page <= 1'b0;
    end else if (!btn[1]) begin
      page <= 1'b1;
    end else begin
      page <= page;
    end
  end

  reg [16:0] cnt;
  localparam CNT_MAX = 17'd99_999;

  always @(posedge clk or negedge rst) begin
    if (!rst) begin
      cnt <= 17'd0;
    end else if (cnt == CNT_MAX) begin
      cnt <= 17'd0;
    end else begin
      cnt <= cnt + 17'd1;
    end
  end

  reg [7:0] r[0:3];

  wire mark = btn[2];

  reg [2:0] state;
  localparam IDLE = 3'b000;
  localparam FETCH = 3'b001;
  localparam OP1 = 3'b010;
  localparam OP2 = 3'b011;
  localparam EXECUTE = 3'b100;

  reg [15:0] ir;
  reg [ 7:0] op1;
  reg [ 7:0] op2;

  always @(posedge clk or negedge rst) begin
    if (!rst) begin
      pc   <= 8'd1;
      r[0] <= 8'h2E;
      r[1] <= 8'h43;
      r[2] <= 8'h58;
      r[3] <= 8'h6D;
      en   <= 8'b0000_0000;
      data <= 32'd0;
      state <= IDLE;
    end else begin

      case (state)
        IDLE: begin
          // if (mark) begin
            state <= FETCH;
          // end else begin
            // state <= IDLE;
          // end
        end

        FETCH: begin
          if (rom_data[15:12] == 4'b0111 || rom_data[15:12] == 4'b1000) begin
            ir <= rom_data;
            state <= OP1;
          end else begin
            state <= IDLE;
          end
        end

        OP1: begin
          op1 <= r[ir[11:10]];
          if (ir[15:12] == 4'b0111) begin
            state <= OP2;
          end else begin
            state <= EXECUTE;
          end
        end

        OP2: begin
          op2   <= r[ir[9:8]];
          state <= EXECUTE;
        end

        EXECUTE: begin
          case (ir[15:12])
            4'b0111: begin
              r[ir[7:6]] <= op1 ^ op2;
            end
            4'b1000: begin
              r[ir[9:8]] <= op1 >> 1'b1;
            end
          endcase
          pc <= pc + 8'd1;
          state <= IDLE;
        end

        default: begin
          state <= IDLE;
        end
      endcase

      if (!page) begin
        en   <= 8'b1111_1111;
        data <= {r[0], r[1], r[2], r[3]};

        if (is_pressed) begin
          case (key_sel)
            1'b0: begin
              r[ra] <= {val, r[ra][3:0]};
            end
            1'b1: begin
              r[ra] <= {r[ra][7:4], val};
            end
          endcase
        end
      end else begin
        en   <= 8'b11001111;
        data <= {rom_data, 8'h0, pc};

        case (m)
          2'b00: begin
            pc <= 8'd0;
          end
          2'b01: begin
            if (cnt == CNT_MAX) begin
              pc <= pc + 8'd1;
            end else begin
              pc <= pc;
            end
          end
          2'b10: begin
            if (cnt == CNT_MAX) begin
              pc <= pc - 8'd1;
            end else begin
              pc <= pc;
            end
          end
          2'b11: begin
            if (is_pressed) begin
              case (key_sel)
                2'b0: begin
                  pc[7:4] <= val;
                end
                2'b1: begin
                  pc[3:0] <= val;
                end
              endcase
            end
          end
        endcase



      end

    end

  end

endmodule
